﻿#include "discrete_acis_utils.hxx"
#include "CDT.h"
#include "CDTUtils.h"
#include "Triangulation.h"

#pragma once
typedef std::vector<CDT::V2d<double>> point_vector;
typedef CDT::V2d<double> point;
typedef std::vector<CDT::Edge> edge_vector;
enum class Direct { left, right };
enum class ParamDirect { U, V };


struct ENT_UV_T_POS {
    explicit ENT_UV_T_POS(ENTITY* _ent, SPAposition* P, const SPApar_pos* uv, double t) noexcept
        : ent_(_ent), coords{ static_cast<float>(uv->u), static_cast<float>(uv->v), static_cast<float>(t), static_cast<float>(P->x()), static_cast<float>(P->y()), static_cast<float>(P->z()) } {
    }

    ENTITY* GetEntity() const noexcept { return ent_; }

    SPApar_pos GetUV() const noexcept { return SPApar_pos{ coords[0], coords[1] }; }

    float t() const noexcept { return coords[2]; }

    SPAposition GetPosition() const noexcept { return SPAposition{ coords[3], coords[4], coords[5] }; }

    ENTITY* ent_;  // belongs to the entity face/edge

    float coords[6];  // u v t x y z, t maybe garbege
};

// The following is used to support the triangulation library implemented in the future
struct UV_VERTEX {
    SPAposition position;
    SPAunit_vector normal;
    SPApar_pos uv;
    UV_VERTEX(SPAposition p, SPAunit_vector n, SPApar_pos _uv) noexcept : position(p), normal(n), uv(_uv) {}
};

struct UV_NODE {  // doubly linked list node
    UV_NODE* next;
    UV_NODE* prev;
    UV_VERTEX* vertex_;
    bool flag;
};

struct UV_SET {  // doubly linked list
    UV_NODE* first;
    UV_NODE* last;
};

struct PPoint {  // Point in the ParamSpace
    SPApar_pos pos;
    SPApar_dir dir;
    std::weak_ptr<PPoint> next;

    PPoint() = default;

    PPoint(SPApar_pos pos_2d) : pos(pos_2d) {}

    void SetNext(std::shared_ptr<PPoint> p) { this->next = p; }

    std::shared_ptr<PPoint> GetNext() const { return next.lock(); }
};

using PPointPtr = std::shared_ptr<PPoint>;
/**
 * @struct FaceterFaceInfo
 * @brief 离散化需要的关于面的一些信息
 */
struct FaceterFaceInfo {
    FACE* face_ptr_;
    surface& surf;
    double u_ri, u_le, v_ri, v_le;
    bool is_singular_ule = false;
    bool is_singular_uri = false;
    bool is_singular_vle = false;
    bool is_singular_vri = false;
    REVBIT sense;
    double u_tolerance, v_tolerance;
    double dv = 0, du = 0;
    bool is_periodic_u{ false }, is_periodic_v{ false };
    FaceterFaceInfo(FACE* face, double u_ri, double u_le, double v_ri, double v_le, double u_tol, double v_tol, bool is_periodic_u = false, bool is_periodic_v = false);
    FaceterFaceInfo(FACE* face);
};


inline double get_cloest_boundary_point(double le, double dx, double p) {
    return le + dx * round((p - le) / dx);
}

/**
 * 计算两点是否在EPSILON精度下是否相等
 */
bool operator==(point a, point b);

bool operator!=(point a, point b);

point operator-(point a, point b);

const double GetX(const point& p);

const double GetY(const point& p);

double resetToInterval(double x, double l, double r, double perio);

/**
 * @brief 对三角形进行后处理，包括边翻转以优化网格。
 *
 * 遍历给定CDT(约束Delaunay三角剖分)中的所有三角形，对每个三角形及其邻居进行检查，
 * 以确定是否需要进行边翻转操作。边翻转的决策基于三角形顶点在参数空间中的位置以及
 * 对应的几何空间中的位置。如果翻转后的对角线能够使得两个三角形的中心点更接近，则执行翻转。
 *              opsv         | - old edge
 *               /|\         ~ - new edge
 *              / | \
 *             /  t  \
 *            /   |   \
 *           /    |    \
 *          v2~~~~~~~~~v1
 *           \    |    /
 *            \   |   /
 *             \nibor/
 *              \ | /
 *               \|/
 *              nopsv
 * @param[in] f 指向当前处理的面的指针。
 * @param[in] nt 角度容差。
 * @param[in] st 距离容差。
 * @param[in] pcdt 指向当前处理的CDT的指针。
 */
void PostProcessTriangle(FACE* f, double nt, double st, CDT::Triangulation<double>* pcdt);
/**
 * 用于面的容差判断的接口.
 * 检查三角形是否满足容差要求
 * pSPApar_pos长度为3，是三角形三个顶点的参数坐标
 * @return 三角形满足容差关系则返回true
 */
bool checkTriangle(FACE* f, double nt, double st, SPApar_pos* pSPApar_pos);
/**
 * 用于面容查判断的接口.
 * 对于输入的CDT结果进行判断并修复不满足容差的三角形
 *
 * @return （暂时）如果没有新增点即所有三角形检查无误则返回true；有新增点返回false。
 */
bool fixTriangle(FACE* f, double nt, double st,          // 参数同上
    CDT::Triangulation<double>* pcdt,       // 三角化结果
    std::vector<CDT::V2d<double>>* ppoints  // 节点列表
);

class abstract_faceter {
protected:
    FACE* f = nullptr;
    double nt = 0, st = 0;

    point_vector points;
    edge_vector edges;
    REVBIT normalR = FORWARD;
    point le_low, ri_high;
    double u_le, u_ri, v_le, v_ri;
    double u_tolerance, v_tolerance;
    int ulen, vlen;
    double du, dv;

    std::vector<point_vector> hole_loops;
    std::vector<point_vector> u_loops;
    std::vector<point_vector> v_loops;
    std::vector<point_vector> uv_loops;
    point_vector periphery;

    virtual void init() {}
    virtual void decideUVlen() {}
    virtual bool getLoops() = 0;
    virtual void peripheryProcess() {}
    virtual bool USeperationProcess() = 0;
    virtual bool VSeperationProcess() = 0;
    virtual void holeProcess() {}

    virtual void attachMesh() {}

public:
    abstract_faceter(FACE* f, double nt = 0, double st = 0) : f(f), nt(nt), st(st) {}
    virtual outcome facet() = 0;
    void error(std::string) {}
};
class Box {
public:
    SPApar_pos bot_left_;
    SPApar_pos top_right_;
    Box(const SPApar_pos& bot_left, const SPApar_pos& top_right) noexcept : bot_left_(bot_left), top_right_(top_right) {}
    const double getLeft() const noexcept { return bot_left_.u; }
    const double getRight() const noexcept { return top_right_.u; }
    const double getTop() const noexcept { return top_right_.v; }
    const double getBot() const noexcept { return bot_left_.v; }
    const double getWidth() const noexcept { return top_right_.u - bot_left_.u; }
    const double getHight() const noexcept { return top_right_.v - bot_left_.v; }
    const SPApar_pos getBotLeft() const noexcept { return bot_left_; }
    const SPApar_pos getBotRight() const noexcept { return SPApar_pos(top_right_.u, bot_left_.v); }
    const SPApar_pos getTopRight() const noexcept { return top_right_; }
    const SPApar_pos getTopLeft() const noexcept { return SPApar_pos(bot_left_.u, top_right_.v); }
    const SPApar_pos getCenter() const noexcept { return SPApar_pos((bot_left_.u + top_right_.u) / 2, (bot_left_.v + top_right_.v) / 2); }
    const SPApar_pos getLeftMid() const noexcept { return SPApar_pos(bot_left_.u, (bot_left_.v + top_right_.v) / 2); }
    const SPApar_pos getRightMid() const noexcept { return SPApar_pos(top_right_.u, (bot_left_.v + top_right_.v) / 2); }
    const SPApar_pos getTopMid() const noexcept { return SPApar_pos((bot_left_.u + top_right_.u) / 2, top_right_.v); }
    const SPApar_pos getBotMid() const noexcept { return SPApar_pos((bot_left_.u + top_right_.u) / 2, bot_left_.v); }
    const SPApar_vec getSize() const noexcept { return top_right_ - bot_left_; }
};

bool IsEdgeDegenerate(EDGE* ed);

/**
 * 检查给定的矩形区域是否可以通过对角线分割成两个三角形，以满足特定的拟合条件。
 *
 * 该函数首先定义了两组三角形，每组对应于矩形沿一个对角线分割的结果。然后，它计算矩形四个角点
 * 和中心点的位置，以及沿两个对角线的中点位置。根据中心点到这两个中点的距离，选择一个对角线
 * 进行分割，并检查沿该对角线分割得到的两个三角形是否都满足给定的角度和距离容差条件。
 *
 * @param[in] box 一个表示矩形区域的Box对象，包含矩形的位置和尺寸信息。
 * @return 返回一个布尔值，表示是否可以通过对角线分割的两个三角形拟合。
 */
bool CheckFitAndJudgeDiagonal(FACE* face, const Box& box, const double angle_tolerance, const double distance_tolerance);

/**
 * @brief 移除重复的点和边，并重新映射边
 *
 * @param points 点集，可能包含重复的点
 * @param edges 边集，可能包含重复的边或者因点重复导致的重复边
 */
void RemoveDuilplicatePointsAndEdges(point_vector& points, edge_vector& edges);

/**
 * @brief 获取多边形与所有边的交点
 *
 * 根据给定的点集和边集，以及多边形的边集和顶点集，计算每条多边形边与扫描线的交点。
 * 可以选择水平或垂直扫描线方向进行计算。
 *
 * @param[in] points 点集，每个点由其坐标表示。
 * @param[in] edges 边集，每条边由其在点集中的起点和终点索引表示。 所有边都必须垂直或者平行于x轴。
 * @param[in] polygon_edges 多边形的边集，每条边由其在多边形顶点集中的起点和终点索引表示。
 * @param[in] polygon_points 多边形的顶点集，每个顶点由其坐标表示。
 * @param[in] get_sweep_cord 获取扫描线轴的坐标的函数。
 * @param[in] get_cord 获取非扫描轴上的坐标的函数。
 * @return std::unordered_map<CDT::Edge, std::vector<double>> 每条边与扫描线的交点坐标集合。
 */
template <typename Fn_1, typename Fn_2>
std::unordered_map<CDT::Edge, std::vector<double>> CalculatePolygonEdgeIntersections(const point_vector& face_points, const edge_vector& face_edges, const edge_vector& polygon_edges, const point_vector& polygon_points, Fn_1 get_sweep_cord, Fn_2 get_cord);
/**
 * @brief 判断每个点是否在多边形内部
 *
 * 根据给定的点集、多边形边集和多边形顶点集，判断每个点是否位于多边形内部。可以选择水平或垂直扫描线方向进行判断。
 *
 * @param[in] points 点集，每个点由其坐标表示。
 * @param[in] polygon_edges 多边形的边集，每条边由其在顶点集中的起点和终点索引表示。
 * @param[in] polygon_points 多边形的点集，每个顶点由其坐标表示。
 * @param[in] get_sweep_cord 获取扫描线轴的坐标的函数。
 * @param[in] get_cord 获取非扫描轴上的坐标的函数
 * @return std::unordered_set<int> 不在多边形内部的点的索引集合。
 */

template <typename Fn_1, typename Fn_2> std::unordered_set<int> JudgePointsInPolygon2D(const point_vector& points, const edge_vector& polygon_edges, const point_vector& polygon_points, Fn_1 get_sweep_cord, Fn_2 get_cord);

void NewIndexedMesh(FACE* f, const std::vector<UV_VERTEX>& vertice, const CDT::TriangleVec& triangles);

////// for debug
void WritePointsToCSV(const std::string& filename, const point_vector& points);

void WriteTrianglesToCSV(const std::string& filename, const CDT::TriangleVec& triangles);

void SaveFaceToSAT(const char* filename, FACE* face);

class DeBugOutputUVGraph {
public:
    void init(FACE* face);
    void AddTriangles(point_vector points, CDT::EdgeUSet edges_set);
    void AddconstraintEdge(std::pair<double, double> pointa, std::pair<double, double> pointb);
    void AddBoundaryPoint(std::pair<double, double> point);
    void AddSamplePoint(std::pair<double, double> point);
    void output();

private:
    std::pair<double, double> b_u, b_v;
    std::vector<std::pair<double, double>> dis_points;
    std::vector<std::pair<int, int>> triangulation_edges;
    std::vector<std::pair<int, int>> constraint_edges;
    std::vector<int> boundary_indices;
    std::vector<int> sample_indices;
};


class BoundaryAnalyzer {
public:
    FaceterFaceInfo info_;

    bndy_type GetSingularType(const SPApar_pos& p, const double tol = SPAresabs = SPAresabs);

    void GetFitParam(SPApar_pos& pos, const double tol = SPAresabs);

    void GetFitParam(SPApar_pos& pos, const SPAposition& pos_3d, const double tol = SPAresabs);

    void GetFitParamBy2D(SPApar_pos& pos, const double tol);

    bool SamePointJudgeBy3D(const SPApar_pos& a, const SPApar_pos& b, const double tol = SPAresabs);

    bool IsSingular(const SPApar_pos& p, const double tol = SPAresabs);

    template <ParamDirect X> bool IsOnBoundary(const SPApar_pos& p, const double tol = SPAresabs);

    bool IsOnBoundary(const SPApar_pos& p, const double tol = SPAresabs);

    bool IsOnBoundary(const SPApar_pos& p, bndy_type type, const double tol = SPAresabs);

    bndy_type GetBoundaryType(const SPApar_pos& p, const double tol = SPAresabs);

    SPApar_pos GetClosestPoint(const SPApar_pos& p, const SPApar_pos& guess);

    template <ParamDirect X> Direct GetDirectRelateBoundary(SPApar_dir dir_2d);

    explicit BoundaryAnalyzer(FACE* _face);
};

logical get_facet_edge_points_and_params(EDGE* edge, SPAposition*& pos_array, double*& param_array, int& num_pts);

outcome api_get_facet_edge_points_test(EDGE* edge, SPAposition*& polyline, int& num_pts, AcisOptions* ao = nullptr);

SPAinterval recover_line_endpoint(const SPAinterval& inter);

void get_face_tolerance(double in_body_diagnoal, facet_options_visualization* fo, double& out_face_distance_tolerance, double& out_face_angle_tolerance);
void get_edge_tolerance(double in_body_diagnoal, facet_options_visualization* fo, double& out_edge_distance_tolerance, double& out_edge_angle_tolerance);
